"""Rudimentary particle engine"""
import pygame, math
import vecmath
import resource_manager
from random import *
from pygame.locals import *

class ParticleSystem(pygame.sprite.Group):
    """Particle system, particles or emitters can be added to it and it will update
    and draw them properly"""
    def __init__(self, limit=150):
        """"limit: the maximun number of particles that will be in this sistem
        at once"""
        pygame.sprite.Group.__init__(self)
        self.limit = limit
        self._emitters = []

    def add_internal(self, sprite):
        if(self.limit > len(self)): # limit check
            pygame.sprite.Group.add_internal(self, sprite)
    
    def add_emitter(self, emitter):
        """Adds an emitter to the sistem, so it can be manged"""
        emitter.engine = self
        emitter.ttl = emitter._ttl
        self._emitters.append(emitter)
    
    def remove_emitter(self, emitter):
        """Removes an emitter from the system"""
        self._emitters.remove(emitter)
    
    def update(self, delta):
        pygame.sprite.Group.update(self)
        for e in self._emitters:
            e.update()

class Emitter(object):
    """Base particle emitter, don't instantiate, subclass
    An Emitter generates a continous stream of particles in a given direction(s)
    for a determinate amount of time.
    Use an emitter when the particles should be flowing for some time without
    interaction or from nowhere, otherwise adding the particles directly to a
    ParticleSystem is faster"""
    def __init__(self, rate, position, angle, speed, ttl = -1):
        """Rate is the amount of particles per tick this Emitter will emit
        Position is the initial position (x, y) of the particles
        Angle is the direction in wich the particles are shot, or a range of angles (min, max)
        Speed at wich the particles will be shot, or a range of speeds (min, max)
        ttl is the number of game ticks to live this emitter, -1 for undefined
        
        If not speed nor angle are tuples, a variable named self.p_velocity will
        be inited to the default (x, y) velocity of the particles"""
        self.engine = None
        self.rate = rate
        self.position = position
        
        if type(angle) is tuple:
            self.p_angle = math.radians(angle[0])
            self.p_angle_max = math.radians(angle[1])
        else:
            self.p_angle = math.radians(angle)
            self.p_angle_max = 0
        if type(speed) is tuple:
            self.p_speed = speed[0]
            self.p_speed_max = speed[1]
        else:
            self.p_speed = speed
            self.p_speed_max = 0
        
        self.p_velocity = 0 # velocidad de las particulas
        if not (self.p_angle_max or self.p_speed_max):
            # si no razon, no se genera aleatoriamente
            self.p_velocity = (math.cos(self.p_angle) * self.p_speed,\
                               math.sin(self.p_angle) * self.p_speed)
            
        self._ttl = ttl
        self.ttl = 0
    
    def update(self):
        if not self.ttl: # alive?
            self.engine.remove_emitter(self)
            return
        elif self.ttl > 0:
            self.ttl -= 1
    
        i = 0
        while i < self.rate:
            i+=1
            self.generate_particle()
    
    def generate_particle(self):
        """Implement this method to generate the stream of particles for this emitter"""
        pass
    
class Particle(pygame.sprite.Sprite):
    """Base particle class. Not for instantiation.
    Subclases may add an image or the particle will be quite dull"""
    _image = pygame.Surface((10, 10))
    def __init__(self, position, velocity, ttl = 100):
        """position is (x, y)
        velocity is (x, y)
        ttl is the number millis this particle will live"""
        pygame.sprite.Sprite.__init__(self)
        self.position = list(position)
        self.velocity = list(velocity)
        self.scale = game.zoom
        self.image = self.image = pygame.transform.rotozoom(self._image, 0, self.scale)
        self.rect = self.image.get_rect(center = ((position[0] * game.zoom) - game.vp[0],\
                                                  (position[1] * game.zoom) - game.vp[1]))
        self.ttl = pygame.time.get_ticks() + ttl

    def update(self):
        if self.ttl < pygame.time.get_ticks():
            self.kill()
            return
        
        self.position[0], self.position[1] = (self.position[0] + self.velocity[0],\
                                              self.position[1] + self.velocity[1])
        
        self.update_image()
            
        self.rect.center = ((self.position[0] * game.zoom) - game.vp[0],\
                            (self.position[1] * game.zoom) - game.vp[1])

    def update_image(self):
        pass

class SmokeEmitter(Emitter):
    """Emitter that will shoot a spray of smoke"""
    def __init__(self, rate, position, angle, speed, ttl = 60):
        """rate is the number of particles shot in one tick
        position is (x, y) of the origin of the particles
        angle is (min, max) is the amount of degrees to turn the particles
            each tick
        speed is (min, max)
        ttl is the number ticks this emitter will live"""
        Emitter.__init__(self, rate, position, angle, speed, ttl)
        self.p_growth_rate = 0.01
        self.p_rot_speed = 10

    def generate_particle(self):
        if(self.p_velocity):
            self.engine.add(SmokeParticle(self.position, self.p_velocity,\
                                          self.p_rot_speed, self.p_growth_rate))
        else:
            a = self.p_angle
            if self.p_angle_max:
                a += self.p_angle_max * random()
            
            s = self.p_speed
            if self.p_speed_max:
                s += self.p_speed_max * random()
            
            v = (0, 0)
            if s:
                v = (math.cos(a) * s, math.sin(a) * s)
            
            self.engine.add(SmokeParticle(tuple(self.position), v,\
                                          self.p_rot_speed,\
                                          self.p_growth_rate))

class SmokeParticle(Particle):
    """Smoke particle will rotate and grow at a certain speed"""    
    _image = resource_manager.load_image('smoke.png')[0]
    def __init__(self, position, velocity, rot_speed, growth_rate, scale = 1, ttl = 250):
        """position is (x, y)
        velocity is (x, y)
        rot_speed is the amount of degrees to turn this sprite (random to the left or right)
            each tick
        growth_rate is an amount that will be added to the scale each tick
        scale is the initial factor that will be applied to the sprite each tick
        ttl is the number millis this particle will live"""
        Particle.__init__(self, position, velocity, ttl)
        self.heading = 0
        self.growth = scale - 1
        self.rot_speed = rot_speed
        self.grow_rate = growth_rate

    def update_image(self):
        rz = 0
        if(self.rot_speed):
            self.heading += self.rot_speed
            rz = 1
        
        scale = self.scale + self.growth
        if(self.scale != game.zoom or self.grow_rate):
            self.scale = game.zoom
            self.growth += self.grow_rate
            scale = self.scale * self.growth
            rz = 1
        
        if rz:
            self.image = pygame.transform.rotozoom(self._image, self.heading, scale)
            self.rect = self.image.get_rect()

class FireParticle(SmokeParticle):
    """identical to the fire particle but with diferent image"""
    _image = resource_manager.load_image('fire.png')[0]

class Explosion(Emitter):
    """shoots many fire particles in every direction"""
    def __init__(self, position, particle_speed, emitter_velocity = (0, 0), ttl = 30):
        """position is (x, y) of the origin of the particles
        particle_speed is (min, max)
        emitter_velocity is (x, y)
        ttl is the number ticks this emitter will live"""
        Emitter.__init__(self, 1, position, (0, 360), particle_speed, ttl)
        self.p_growth_rate = 0.1
        self.p_rot_speed = 1
        self.vel = emitter_velocity

    def update(self):
        Emitter.update(self)
        self.position = vecmath.add(self.position, self.vel)
        

    def generate_particle(self):
        a = self.p_angle
        if self.p_angle_max:
            a += self.p_angle_max * random()
        
        s = self.p_speed
        if self.p_speed_max:
            s += self.p_speed_max * random()
        
        v = (0, 0)
        if s:
            v = (math.cos(a) * s, math.sin(a) * s)
        
        self.engine.add(FireParticle(tuple(self.position), v, self.p_rot_speed,\
                                     self.p_growth_rate, 2, 750))
        
class BulletHit(pygame.sprite.Sprite):
    """Not really a particle but an effect sprite"""
    _image = resource_manager.load_image('hit.png')[0]
    def __init__(self, position):
        pygame.sprite.Sprite.__init__(self)
        self.image = self._image
        self.rect = self._image.get_rect(center = ((position[0] * game.zoom) - game.vp[0],\
                                                   (position[1] * game.zoom) - game.vp[1]))
        self.ttl = 10
    
    def update(self):
        if not self.ttl:
            self.kill()
        self.ttl -= 1
        self.image = pygame.transform.rotozoom(self.image, 0, game.zoom)

import game